package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.zzyl.constant.ResouceCacheConstant;
import com.zzyl.constant.SuperConstant;
import com.zzyl.dto.ResourceAddDto;
import com.zzyl.dto.ResourceListDTO;
import com.zzyl.entity.Resource;
import com.zzyl.enums.BasicEnum;
import com.zzyl.exception.BaseException;
import com.zzyl.mapper.ResourceMapper;
import com.zzyl.service.ResourceService;
import com.zzyl.utils.NoProcessing;
import com.zzyl.vo.MenuItemVo;
import com.zzyl.vo.TreeItemVo;
import com.zzyl.vo.TreeVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import static com.zzyl.constant.SuperConstant.*;
@Slf4j
@Service
public class ResourceServiceImpl implements ResourceService {

    @Autowired
    private ResourceMapper resourceMapper;
    @Override
    public List<MenuItemVo> getMenuByUserId(Long userId) {
        // 先根据userId查询用户的所有的菜单 m
        List<Resource> menuList = resourceMapper.selectByUserId(userId, DATA_STATE_0,  MENU);
        if(CollectionUtils.isEmpty(menuList)){
            return new ArrayList<>();
        }
        // 组织成tree结构
        // 把Resource转化成MenuItemVo
        List<MenuItemVo> voList = new ArrayList<>();
        for (Resource resource : menuList){
            MenuItemVo vo = BeanUtil.copyProperties(resource,MenuItemVo.class);
            vo.setName(resource.getResourceName());
            vo.setPath(resource.getRequestPath());
            vo.setRedirect("/" + resource.getResourceName());
            vo.setChildren(new ArrayList<>());
            vo.setMeta(new MenuItemVo.Meta(resource.getResourceName(),resource.getIcon()));
            voList.add(vo);
        }
        // 构造根节点
        MenuItemVo root = new MenuItemVo();
        root.setResourceNo(ROOT_PARENT_ID);
        root.setChildren(new ArrayList<>());
        // voList转化成树型结构
        tree(root, voList);
        return root.getChildren();
        //return null;
    }
    private void tree(MenuItemVo parent, List<MenuItemVo> voList){
        for (MenuItemVo vo : voList) {
            if(Objects.equals(vo.getParentResourceNo(), parent.getResourceNo())){
                parent.getChildren().add(vo);
                tree(vo, voList);
            }
        }
    }

    @Override
    public List<Resource> getByUserId(Long userId) {
        return resourceMapper.selectByUserId(userId, null, null);
    }


    @Override
    @Caching(
            evict = {
                    @CacheEvict(cacheNames = ResouceCacheConstant.TREE,  key = "''"),
                    @CacheEvict(cacheNames = ResouceCacheConstant.LIST, allEntries = true)

            }
    )
    public void add(ResourceAddDto dto) {
        //dto转entity
        Resource resource = BeanUtil.copyProperties(dto, Resource.class);
        // 判断父菜单是否大于2级
        String s = NoProcessing.processString(dto.getParentResourceNo());
        if(s.length() > 12){
            //说明父菜单已经是3级菜单
            throw  new BaseException(BasicEnum.RESOURCE_DEPTH_UPPER_LIMIT);
        }
        // 生成resource_no
        String resourceNo = null;
        // 先判断父菜单有没有子菜单
        Resource maxChild = resourceMapper.selectMaxNoChild(dto.getParentResourceNo());
        // 如果没有，父菜单的编号 + 001
        if(maxChild == null){
            resourceNo = NoProcessing.createNo(dto.getParentResourceNo(), false);
        }else{
            // 如果有，父菜单下的编号最大的子菜单的编号，+1生成兄弟编号
            resourceNo = NoProcessing.createNo(maxChild.getResourceNo(), true);
        }
        resource.setResourceNo(resourceNo);
        resourceMapper.insertSelective(resource);
    }

    @Override
    @Cacheable(cacheNames = ResouceCacheConstant.LIST, key = "#dto.hashCode()" )
    public List<Resource> list(ResourceListDTO dto) {
        return resourceMapper.list(dto);
    }

    @Override
    @Cacheable(cacheNames = ResouceCacheConstant.TREE, key="''")
    public TreeVo tree() {
        // 查询出拉平的所有的菜单， data_state=0 resource_type=m parent_resource_no like 100001%
        String dataState = SuperConstant.DATA_STATE_0;
        String resourceType = MENU;
        String parentResourceNo = NoProcessing.processString(SuperConstant.ROOT_PARENT_ID);
        List<Resource> resources = resourceMapper.tree(dataState, resourceType, parentResourceNo);
        // 拉平的结构转化成树形结构
        TreeVo treeVo = new TreeVo();
        // 根节点
        TreeItemVo rootItem = new TreeItemVo();
        rootItem.setId(SuperConstant.ROOT_PARENT_ID);
        rootItem.setLabel(SuperConstant.ROOT_PARENT_LABEL);
        if(CollectionUtils.isEmpty(resources)){
            return treeVo;
        }
        // 把数据库中resource转化成TreeItemVo
        List<TreeItemVo> treeItemVos = resourceToItemVo(resources);
        // 树形处理，从所有的菜单中找rootItem的子菜单
        tree(rootItem, treeItemVos);
        treeVo.setItems(Arrays.asList(rootItem));
        return treeVo;
    }

    /**
     * 从所有的菜单中找parentItem的子菜单
     * */
    private void  tree(TreeItemVo parentItem, List<TreeItemVo> treeItemVos){
        String parentId = parentItem.getId();
        for (TreeItemVo treeItemVo : treeItemVos) {
            if(Objects.equals(parentId, treeItemVo.getParentId())){
                parentItem.getChildren().add(treeItemVo);
                // 递归，从所有的菜单中找treeItemVo的子菜单
                tree(treeItemVo,  treeItemVos);
            }
        }
    }

    private List<TreeItemVo> resourceToItemVo(List<Resource> resources) {
        List<TreeItemVo> voList = new ArrayList<>();
        for (Resource resource : resources) {
            TreeItemVo vo = new TreeItemVo();
            vo.setId(resource.getResourceNo());
            vo.setLabel(resource.getResourceName());
            vo.setParentId(resource.getParentResourceNo());
            vo.setChildren(new ArrayList<>());
            voList.add(vo);
        }
        return voList;
    }
}